home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / plan / src / dbase.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  19KB  |  621 lines

  1. /*
  2.  * list management: all entries are kept in lists. A list is a header
  3.  * followed by an array of entries. List sizes are dynamic. Array space
  4.  * is allocated in multiples of CHUNK; if space runs out the list is
  5.  * re-allocated. Copying is done the simple way; let's hope lists don't
  6.  * get too big. There is room for improvement here.
  7.  *
  8.  * Normally, there is one "master list" with everything in the calendar
  9.  * file, from which sublists are created as needed, for example everything
  10.  * on a particular day. Sublists are managed in sublist.c.
  11.  *
  12.  *    create_list(list)        Start a new list.
  13.  *    add_entry(list, entry)        Add an entry to a list. This may
  14.  *                    require realloc'ing the list.
  15.  *    delete_entry(list, n)        Delete an entry from the list.
  16.  *    resort_entry(list, n)        Move a particular (newly added or
  17.  *                    changed) entry to its proper place.
  18.  *    rebuild_repeat_chain(list)    chain all repeating entries in the
  19.  *                    list.
  20.  *
  21.  *    lookup_entry(lookup, list, time, exact, norep)
  22.  *                    Find an entry in the list by date.
  23.  *                    Either finds first, or insert place.
  24.  *    lookup_next_entry(lookup)        Find the next entry.
  25.  *
  26.  *    find_next_trigger(list, time, entryp, wait_time)
  27.  *                    Find next alarm or warn trigger time.
  28.  *    clone_entry(new, old)        Copy one entry to another, and all
  29.  *                    malloced strings in it.
  30.  *    destroy_entry(entry)        Free all malloced strings of an entry.
  31.  */
  32.  
  33. #ifndef MIPS
  34. #include <stdlib.h>
  35. #endif
  36. #include <time.h>
  37. #include <Xm/Xm.h>
  38. #include "cal.h"
  39.  
  40. #define CHUNK        100        /* pointer list allocation unit */
  41.  
  42. extern void        fatal();
  43. extern char        *mystrdup();
  44. extern int        recycle();
  45.  
  46. BOOL            lookup_entry(), lookup_next_entry();
  47.  
  48. time_t            cutoff;        /* no alarms before this time */
  49.                     /* (used by daemon only) */
  50.  
  51.  
  52. /*----------------------------------- list mgmt -----------------------------*/
  53. /*
  54.  * create an empty list, with CHUNK blank entries. When the blank entries
  55.  * are all filled, add_entry will allocate more. It's usually enough though.
  56.  */
  57.  
  58. create_list(list)
  59.     struct list        **list;        /* pointer to list pointer */
  60. {
  61.     if (!(*list = (struct list *)malloc(sizeof(struct list) +
  62.                         sizeof(struct entry) * CHUNK)))
  63.         fatal("no memory");
  64.     (*list)->modified  = FALSE;
  65.     (*list)->locked    = FALSE;
  66.     (*list)->nentries  = 0;
  67.     (*list)->size      = CHUNK;
  68.     (*list)->repeating = -1;
  69. }
  70.  
  71.  
  72. /*
  73.  * destroy a list, by freeing all its entries and then the list itself.
  74.  * This is used by the daemon only, which re-reads the database when it
  75.  * gets a HUP signal. Ignore the lint complaint.
  76.  */
  77.  
  78. destroy_list(list)
  79.     struct list        **list;        /* pointer to list pointer */
  80. {
  81.     register int        i;        /* entry counter */
  82.  
  83.     if (!*list)
  84.         return;
  85.     (*list)->locked++;
  86.     for (i=0; i < (*list)->nentries; i++)
  87.         destroy_entry(&(*list)->entry[i]);
  88.     (*list)->nentries = 0;
  89.     free(*list);
  90.     *list = 0;
  91. }
  92.  
  93.  
  94. /*
  95.  * add an entry to a list. The list is kept sorted. Since the list can grow,
  96.  * a pointer to a pointer is passed; the pointed-to pointer changes if the
  97.  * list is re-allocated (or allocated for the first time). To start a new
  98.  * list, pass a pointer to a null pointer; to get rid of a list, free() it.
  99.  * Returns the index to the new entry in the list.
  100.  * Don't forget to call rebuild_repeat_chain(), because this routine is
  101.  * trashing the chain.
  102.  */
  103.  
  104. add_entry(list, entry)
  105.     struct list        **list;        /* list to add to */
  106.     struct entry        *entry;        /* entry to add */
  107. {
  108.     int            num = 0;    /* # of entries in old list */
  109.     int            size = 0;    /* size of allocated old list*/
  110.     struct list        *new;        /* larger list if required */
  111.     struct lookup        lookup;        /* result of entry lookup */
  112.     int            n;        /* index of new entry in list*/
  113.     register struct entry    *p, *q;        /* entry copy pointers */
  114.     register int        i;        /* entry copy counter */
  115.  
  116.     if (*list) {
  117.         (*list)->locked++;
  118.         num  = (*list)->nentries;
  119.         size = (*list)->size;
  120.     }
  121.     if (num+1 > size) {                /* need larger list */
  122.         size += CHUNK;
  123.         if (!(new = (struct list *)malloc(sizeof(struct list) +
  124.                           sizeof(struct entry) *size)))
  125.             fatal("no memory");
  126.         if (!*list)
  127.             new->nentries = 0;        /* start new list... */
  128.         else {
  129.             new->nentries = num;        /* ...or copy old */
  130.             p = (*list)->entry;
  131.             q = new->entry;
  132.             for (i=0; i < num; i++)
  133.                 *q++ = *p++;
  134.         }
  135.         new->size = size;
  136.         new->locked = 1;
  137.         if (*list)                /* discard old list */
  138.             free(*list);
  139.         *list = new;
  140.     }
  141.     new = *list;                    /* insert entry */
  142.     n = lookup_entry(&lookup, new, entry->time, FALSE, TRUE)
  143.                 ? lookup.index : new->nentries;
  144.     p = &new->entry[n];
  145.     q = &new->entry[new->nentries];
  146.     for (i=new->nentries; i > n; i--, q--)
  147.         q[0] = q[-1];
  148.     clone_entry(p, entry);
  149.     new->nentries++;
  150.     new->repeating = 0;
  151.     new->modified = TRUE;
  152.     new->locked--;
  153.     return(n);
  154. }
  155.  
  156.  
  157. /*
  158.  * delete an entry from a list. The list is never re-allocated because lists
  159.  * never shrink, so a simple pointer to a list is expected, and the index to
  160.  * the entry to be deleted.
  161.  * Call rebuild_repeat_chain() afterwards, this routine trashes the chain.
  162.  */
  163.  
  164. delete_entry(list, n)
  165.     struct list        *list;        /* list to delete from */
  166.     int            n;        /* index to entry to delete */
  167. {
  168.     register struct entry    *q;        /* entry copy pointer */
  169.     register int        i;        /* entry copy counter */
  170.  
  171.     list->locked++;
  172.     if (n < 0 || n >= list->nentries) {
  173.         list->locked--;
  174.         return;
  175.     }
  176.     destroy_entry(q = &list->entry[n]);
  177.     for (i=n; i < list->nentries; i++, q++)
  178.         q[0] = q[1];
  179.     list->nentries--;
  180.     list->repeating = 0;
  181.     list->modified = TRUE;
  182.     list->locked--;
  183. }
  184.  
  185.  
  186. /*
  187.  * if the time of an entry changed, the list has to be re-sorted. Copy a
  188.  * block if entries to fill the hole, and move the changed entry.
  189.  * Returns the index to the moved entry.
  190.  * Call rebuild_repeat_chain() afterwards, this routine trashes the chain.
  191.  */
  192.  
  193. resort_entry(list, n)
  194.     struct list        *list;        /* list to re-sort */
  195.     int            n;        /* index to changed entry */
  196. {
  197.     struct lookup        lookup;        /* result of entry lookup */
  198.     register struct entry    *p;        /* entry copy pointer */
  199.     register int        i;        /* entry copy counter */
  200.     struct entry        save;        /* to save changed entry */
  201.     int            d;        /* new destination index */
  202.  
  203.     list->locked++;
  204.     if (n < 0 || n >= list->nentries) {
  205.         list->locked--;
  206.         return(0);
  207.     }
  208.     p = &list->entry[n];
  209.     d = lookup_entry(&lookup, list, p->time, FALSE, TRUE)
  210.                 ? lookup.index : list->nentries;
  211.     if (d == n) {                    /* no change? */
  212.         list->locked--;
  213.         return(n);
  214.     }
  215.     save = *p;
  216.     if (d < n)                    /* move back in time?*/
  217.         for (i=d; i < n; i++, p--)
  218.             p[0] = p[-1];
  219.     else                        /* move forward? */
  220.         for (i= --d; i > n; i--, p++)
  221.             p[0] = p[1];
  222.     *p = save;
  223.     list->repeating = 0;
  224.     list->modified = TRUE;
  225.     list->locked--;
  226.     return(d);
  227. }
  228.  
  229.  
  230. /*
  231.  * rebuild the chain of repeating entries. Repeating entries are chained. The
  232.  * chain is anchored in the entry list struct. Repeating entries should appear
  233.  * in all day boxes and entry lists they will appear in during their lifetime.
  234.  * The idea is that there are probably relatively few repeating entries, and
  235.  * they can be found by following the chain. All other entries can still be
  236.  * found using binary searches.
  237.  * Rebuilding could be part of add_entry(), but that would be inefficient
  238.  * when a file is read in, because then it's sufficient to build the chain
  239.  * once at EOF, not every time an entry is added.
  240.  */
  241.  
  242. rebuild_repeat_chain(list)
  243.     struct list        *list;        /* list to delete from */
  244. {
  245.     register long        *prev;        /* previous chain pointer */
  246.     register struct entry    *ep;        /* scan pointer */
  247.     register int        n;        /* entry counter */
  248.  
  249.     if (list) {
  250.         list->locked++;
  251.         prev = &list->repeating;
  252.         for (n=0, ep=list->entry; n < list->nentries; n++, ep++)
  253.             if (ep->rep_every || ep->rep_weekdays ||
  254.                 ep->rep_days  || ep->rep_yearly) {
  255.                 *prev = n;
  256.                 prev = &ep->nextrep;
  257.             }
  258.         *prev = -1;
  259.         list->locked--;
  260.     }
  261. }
  262.  
  263.  
  264. /*
  265.  * find an entry with a given time. if <exact> is TRUE, return the index of
  266.  * the first matching entry (this is a straight lookup). If <exact> is FALSE,
  267.  * return the index of the first entry after the last matching entry (this is
  268.  * for inserting, always append if there are multiple actors with the same
  269.  * time). If there is no match, return the index of the first entry with a
  270.  * larger time, regardless of <exact>.
  271.  * If <norep> is TRUE, ignore later instances of repeating entries. This
  272.  * is used when new entries are inserted into the list; when looking up
  273.  * an entry for printing, it is FALSE. Setting <norep> to TRUE also prevents
  274.  * list->repeating to be used before the chain exists.
  275.  * The lookup is a two-step process: entries at a particular date are found
  276.  * using a binary search; the list is sorted by time. Future instances of
  277.  * repeating entries cannot be found that way; they are found by evaluating
  278.  * all repeating entries. Repeating entries are found by following the
  279.  * <nextrep> chain that was built by rebuild_repeat_chain().
  280.  * Return FALSE if the lookup failed, and we fell off the end of the list.
  281.  */
  282.  
  283. static lookup_regular_entry(), lookup_repeating_entry();
  284.  
  285. BOOL lookup_entry(lookup, list, time, exact, norep)
  286.     register struct lookup    *lookup;    /* return struct */
  287.     register struct list    *list;        /* list of entries */
  288.     time_t            time;        /* time to locate */
  289.     BOOL            exact;        /* need entry, not insert pt */
  290.     BOOL            norep;        /* ignore instances */
  291. {
  292.     int            repindex, regindex;
  293.     time_t            reptime = -1, regtime = -1;
  294.  
  295.     list->locked++;
  296.     lookup->regindex = lookup->repindex = -1;
  297.     lookup->regtime  = lookup->reptime  = 0;
  298.     regindex = lookup_regular_entry(list, time, exact, norep);
  299.     repindex = norep ? -1
  300.              : lookup_repeating_entry(list, time, exact, &reptime);
  301.     if (regindex >= 0)
  302.         regtime = list->entry[regindex].time;
  303.     list->locked--;
  304.  
  305.     if (repindex >= 0 && (regtime < 0 || reptime < regtime)) {
  306.         lookup->index   = lookup->repindex = repindex;
  307.         lookup->trigger = lookup->reptime  = reptime;
  308.     } else {
  309.         lookup->index   = lookup->regindex = regindex;
  310.         lookup->trigger = lookup->regtime  = regtime;
  311.     }
  312.     lookup->list = list;
  313.     return(lookup->index >= 0 && lookup->index < list->nentries);
  314. }
  315.  
  316.  
  317. static lookup_regular_entry(list, time, exact, norep)
  318.     struct list        *list;        /* list of entries */
  319.     time_t            time;        /* time to locate */
  320.     BOOL            exact;        /* need entry, not insert pt */
  321.     BOOL            norep;        /* treat repeaters as regular*/
  322. {
  323.     register struct entry    *entry;        /* entry array */
  324.     register int        lo, hi, mid;    /* binary search indices */
  325.  
  326.     entry = list->entry;
  327.     lo = 0;
  328.     hi = list->nentries;
  329.     while (lo < hi) {
  330.         mid = lo + (hi - lo) / 2;
  331.         if (mid == list->nentries || time >= entry[mid].time)
  332.             lo = mid+1;
  333.         else
  334.             hi = mid;
  335.     }
  336.     if (exact)
  337.         while (lo && time == entry[lo-1].time)
  338.             lo--;
  339.     else
  340.         while (lo < list->nentries && time == entry[lo].time)
  341.             lo++;
  342.     if (!norep)
  343.         while (lo < list->nentries && (entry[lo].rep_every    ||
  344.                            entry[lo].rep_yearly   ||
  345.                            entry[lo].rep_weekdays ||
  346.                            entry[lo].rep_days))
  347.             lo++;
  348.     return(lo < list->nentries ? lo : -1);
  349. }
  350.  
  351.  
  352. static lookup_repeating_entry(list, time, exact, reptime)
  353.     struct list        *list;        /* list of entries */
  354.     time_t            time;        /* time to locate */
  355.     BOOL            exact;        /* need entry, not insert pt */
  356.     time_t            *reptime;    /* returned repeat time */
  357. {
  358.     register struct entry    *entry;        /* entry array */
  359.     register int        n;        /* next repeating entry */
  360.     register time_t        earliest = 0;    /* earliest future entry yet */
  361.     register int        earliest_n= -1;    /* index of that entry */
  362.     int            prev_n = -1;    /* entry before n */
  363.     int            earliest_prev_n;/* entry before earliest_n */
  364.     time_t            test;        /* next repeater trigger */
  365.  
  366.     exact = exact != FALSE;
  367.     for (n=list->repeating; n != -1; prev_n=n, n=entry->nextrep) {
  368.         entry = &list->entry[n];
  369.         if (recycle(entry, time, &test) == 1)
  370.             if (!earliest || test + exact < earliest) {
  371.                 earliest   = test;
  372.                 earliest_n = n;
  373.                 earliest_prev_n = prev_n;
  374.             }
  375.     }
  376.     if (earliest_n >= 0 && earliest_prev_n >= 0) {
  377.         list->entry[earliest_prev_n].nextrep =
  378.                     list->entry[earliest_n].nextrep;
  379.         list->entry[earliest_n].nextrep = list->repeating;
  380.         list->repeating = earliest_n;
  381.     }
  382.     *reptime = earliest;
  383.     return(earliest_n);
  384. }
  385.  
  386.  
  387. /*
  388.  * given an entry, find the entry that triggers next. If there is none,
  389.  * return FALSE.
  390.  * The trigger time of the returned entry is stored in <lookup.trigger>.
  391.  * Never call this when the previous call or lookup_entry() returned FALSE.
  392.  *
  393.  * First, if the previously returned entry was repeating, go to the next
  394.  * repeating entry; if the previously returned entry was regular, go to
  395.  * the next regular entry. Then pick the earlier of the two.
  396.  */
  397.  
  398. lookup_next_entry(lookup)
  399.     register struct lookup    *lookup;    /* previously found entry */
  400. {
  401.     register struct entry    *entry;        /* entry array */
  402.     register int        n;        /* next repeating entry */
  403.     int            repindex = -1, regindex = -1;
  404.     time_t            reptime, regtime;
  405.     struct list        *list = lookup->list;
  406.     time_t            time = 0;
  407.     int            prev_n, earliest_prev_n = -1;
  408.     register time_t        earliest = 0;
  409.     register int        earliest_n = -1;
  410.  
  411.                             /* next repeating */
  412.     prev_n = lookup->repindex >= 0 ? lookup->repindex
  413.                        : list->repeating;
  414.     n = prev_n == -1 ? -1 : list->entry[prev_n].nextrep;
  415.     if (lookup->repindex >= 0) {
  416.         prev_n = lookup->repindex;
  417.         n      = list->entry[prev_n].nextrep;
  418.     } else {
  419.         prev_n = -1;
  420.         n      = list->repeating;
  421.     }
  422.     for (; n != -1; prev_n=n, n=entry->nextrep) {
  423.         entry = &list->entry[n];
  424.         if (recycle(entry, lookup->trigger, &time) == 1)
  425.             if (!earliest || time < earliest) {
  426.                 earliest   = time;
  427.                 earliest_n = n;
  428.                 earliest_prev_n = prev_n;
  429.             }
  430.     }
  431.     repindex = earliest_n;
  432.     reptime  = earliest;
  433.     entry = &list->entry[n = earliest_n];
  434.     if (n >= 0 && earliest_prev_n >= 0) {
  435.         if (earliest_prev_n == -1)
  436.             list->repeating = entry->nextrep;
  437.         else
  438.             list->entry[earliest_prev_n].nextrep
  439.                     = entry->nextrep;
  440.         if (lookup->repindex == -1) {
  441.             entry->nextrep = list->repeating;
  442.             list->repeating = n;
  443.         } else {
  444.             entry->nextrep =
  445.                 list->entry[lookup->repindex].nextrep;
  446.             list->entry[lookup->repindex].nextrep = n;
  447.         }
  448.     }
  449.  
  450.                             /* next regular */
  451.     if (lookup->regindex < 0) {
  452.         regindex = lookup_regular_entry(list,
  453.                 lookup->trigger, TRUE, FALSE);
  454.         regtime = list->entry[regindex].time;
  455.     } else {
  456.         n = lookup->regindex + 1;
  457.         entry = &list->entry[n];
  458.         for (; n < list->nentries; n++, entry++)
  459.             if (!entry->rep_every && !entry->rep_weekdays &&
  460.                 !entry->rep_days  && !entry->rep_yearly) {
  461.                 regindex = n;
  462.                 regtime  = entry->time;
  463.                 break;
  464.             }
  465.     }
  466.                             /* use rep or reg */
  467.     if (repindex >= 0 && (regindex < 0 || reptime < regtime)) {
  468.         lookup->index    = lookup->repindex = repindex;
  469.         lookup->trigger  = lookup->reptime  = reptime;
  470.     } else {
  471.         lookup->index    = lookup->regindex = regindex;
  472.         lookup->trigger  = lookup->regtime  = regtime;
  473.     }
  474.     return(lookup->index >= 0 && lookup->index < list->nentries);
  475. }
  476.  
  477.  
  478. /*
  479.  * scan main list, and return the next entry. The next entry is the one
  480.  * triggers next for one of three reasons (returned value):
  481.  *  0: no trigger for at least 24 hours after <time>,
  482.  *  1: early_warn of *entryp is nonzero, and early warn time is reached,
  483.  *  2: late_warn of *entryp is nonzero, and late warn time is reached,
  484.  *  3: the alarm time of *entryp is reached.
  485.  *
  486.  * The algorithm uses the fact that no warning can come more than 24 hours
  487.  * before the trigger time. The first entry that is at most 24 hours early
  488.  * is looked up, then the next 72 hours are searched linearly. Events that
  489.  * <triggered> before are ignored. The caller must store the returned reason
  490.  * in (*entryp)->triggered to avoid double triggers, iff the reason is > 0.
  491.  * wait can be negative, up to -ANCIENT, to catch events that were missed.
  492.  *
  493.  * This routine is used by the daemon only. Ignore the lint complaint.
  494.  */
  495.  
  496. find_next_trigger(list, time, entryp, wait_time)
  497.     struct list        *list;        /* list of entries */
  498.     time_t            time;        /* time to locate */
  499.     struct entry        **entryp;    /* ret: entry that was found */
  500.     time_t            *wait_time;    /* ret: how long until triggr*/
  501. {
  502.     BOOL            found;        /* TRUE if lookup succeeded */
  503.     struct lookup        lookup;        /* result of entry lookup */
  504.     register struct entry    *ep;        /* current entry */
  505.     time_t            trigger;    /* next trigger time of *ep */
  506.     time_t            dist;        /* time distance to event */
  507.     time_t            mindist = MAXT;    /* smallest distance so far */
  508.     int            reason = 0;    /* why is it earliest: 1,2,3 */
  509.  
  510.     if (!list)
  511.         return(0);
  512.  
  513.     found = lookup_entry(&lookup, list, time-ANCIENT, TRUE, FALSE);
  514.     while (found) {
  515.         trigger = lookup.trigger;
  516.         if (trigger > time+2*86400)
  517.             break;
  518.         ep = list->entry + lookup.index;
  519.         if (!ep->suspended && !ep->notime)
  520.             switch (ep->triggered) {
  521.               case 0:
  522.                 if (ep->early_warn &&
  523.                     trigger - ep->early_warn > cutoff) {
  524.                     dist = trigger - ep->early_warn - time;
  525.                     if (dist > -ANCIENT && dist < mindist){
  526.                         mindist = dist;
  527.                         *entryp = ep;
  528.                         reason  = 1;
  529.                     }
  530.                 }
  531.               case 1:
  532.                 if (ep->late_warn &&
  533.                     trigger - ep->late_warn > cutoff) {
  534.                     dist = trigger - ep->late_warn - time;
  535.                     if (dist > -ANCIENT && dist < mindist){
  536.                         mindist = dist;
  537.                         *entryp = ep;
  538.                         reason  = 2;
  539.                     }
  540.                 }
  541.               case 2:
  542.                 if (!ep->noalarm && trigger > cutoff) {
  543.                     dist = trigger - time;
  544.                     if (dist > -ANCIENT && dist < mindist){
  545.                         mindist = dist;
  546.                         *entryp = ep;
  547.                         reason  = 3;
  548.                     }
  549.                 }
  550.             }
  551.         found = lookup_next_entry(&lookup);
  552.     }
  553.     *wait_time = mindist;
  554.     return(reason);
  555. }
  556.  
  557.  
  558. /*----------------------------------- entry mgmt ----------------------------*/
  559. /*
  560.  * duplicate an entry. This is done when someone begins to edit an entry;
  561.  * the old entry is preserved and stays in the list until the new entry is
  562.  * confirmed (see confirm_new_entry()). If <old> is 0, the new entry is
  563.  * zeroed.
  564.  */
  565.  
  566. clone_entry(new, old)
  567.     register struct entry    *new;        /* entry to fill in */
  568.     register struct entry    *old;        /* entry to clone, or 0 */
  569. {
  570.     if (!old) {                    /* zero new entry */
  571.         new->time         = 0;
  572.         new->length       = 0;
  573.         new->early_warn   = 0;
  574.         new->late_warn    = 0;
  575.         new->suspended    = FALSE;
  576.         new->private      = FALSE;
  577.         new->noalarm      = FALSE;
  578.         new->notime       = FALSE;
  579.         new->triggered    = 0;
  580.         new->message      = 0;
  581.         new->script       = 0;
  582.         new->meeting      = 0;
  583.         new->note         = 0;
  584.         new->rep_every    = 0;
  585.         new->rep_last     = 0;
  586.         new->rep_weekdays = 0;
  587.         new->rep_days     = 0;
  588.         new->rep_yearly   = FALSE;
  589.         return;
  590.     }
  591.     *new = *old;                    /* copy old to new */
  592.     if (old->message)
  593.         new->message = mystrdup(old->message);
  594.     if (old->script)
  595.         new->script  = mystrdup(old->script);
  596.     if (old->meeting)
  597.         new->meeting = mystrdup(old->meeting);
  598.     if (old->note)
  599.         new->note    = mystrdup(old->note);
  600. }
  601.  
  602.  
  603. /*
  604.  * destroy an entry. De-allocate all memory hanging off it. Don't release
  605.  * the entry itself, it's probably some static buffer.
  606.  */
  607.  
  608. destroy_entry(entry)
  609.     register struct entry    *entry;        /* entry to destroy */
  610. {
  611.     if (entry->message)
  612.         free(entry->message);
  613.     if (entry->script)
  614.         free(entry->script);
  615.     if (entry->meeting)
  616.         free(entry->meeting);
  617.     if (entry->note)
  618.         free(entry->note);
  619.     clone_entry(entry, (struct entry *)0); /* paranoid */
  620. }
  621.